﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DynamicToSql
{
    public sealed class Criteria
    {
        public Criteria()
            :this(BooleanOperator.And)
        {
            
        }

        public Criteria(Expression expression)
            :this(BooleanOperator.And)
        {
            this.Add(expression);
        }

        public Criteria(object criteria)
            :this(BooleanOperator.And)
        {
            foreach (var p in criteria.GetType().GetProperties())
                Add(Entity.Property(p.Name) == p.GetValue(criteria, null));
        }

        public Criteria(BooleanOperator booleanOperator)
        {
            this.Criterions = new List<object>();
            this.BooleanOperator = booleanOperator;
        }

        public Criteria Add(Expression expression)
        {
            Criterions.Add(expression);
            return this;
        }

        public Criteria Add(Criteria criteria)
        {
            Criterions.Add(criteria);
            return this;
        }

        public static Criteria operator &(Criteria criteria, Expression expression)
        {
            return MergeQueryExpression(criteria, expression, BooleanOperator.And);
        }

        public static Criteria operator &(Expression expression, Criteria criteria)
        {
            return MergeQueryExpression(criteria, expression, BooleanOperator.And);
        }

        public static Criteria operator |(Criteria criteria, Expression expression)
        {
            return MergeQueryExpression(criteria, expression, BooleanOperator.Or);
        }

        public static Criteria operator |(Expression expression, Criteria criteria)
        {
            return MergeQueryExpression(criteria, expression, BooleanOperator.Or);
        }

        private static Criteria MergeQueryExpression(Criteria criteria, Expression expression, BooleanOperator booleanOperator)
        {
            if (criteria.BooleanOperator == booleanOperator)
            {
                criteria.Add(expression);
                return criteria;
            }
            else
            {
                Criteria newQuery = new Criteria(booleanOperator);
                newQuery.Add(criteria);
                newQuery.Add(expression);
                return newQuery;
            }
        }

        internal BooleanOperator BooleanOperator { get; set; }

        internal IList<object> Criterions { get; set; }
    }
}
